import UIKit
//函数 特定任务的代码块 在Swift中，每个函数都有一种类型，包括函数的参数值和返回值。你可以把函数当普通变量一样处理

//函数的定义和调用
func sayHello(personName: String) -> String {
    let greeting = "hello, " + personName + "!"
    return greeting
}

let r = sayHello(personName: "xiaoming")
print(r)
//func 开头，返回类型前用 ->
//函数参数和返回值
//1.多重输入参数
func halfOpenRangeLength(start: Int, end: Int) -> Int {
    return end - start
}

halfOpenRangeLength(start: 1, end: 10)

//2.无参函数
func sayHelloWorld() -> String {
    return "hello,world"
}
print(sayHelloWorld())

//3.无返回值 没有定义返回值的函数会返回特殊的值叫 Void,是空元祖
func sayGoodBay(personName: String) {
    print("Goodbye, \(personName)")
}

sayGoodBay(personName: "cw")
//4.多重返回值 使用元祖类型让多个值作为一个复合值返回
func count(string: String) -> (vowels:Int, consoants:Int, others: Int) {
    var vowelss = 0, consoantss = 0, otherss = 0
    for char in string {
        switch char {
        case "a", "e", "i", "o", "u" :
            vowelss += 1
        case "b","c","d","f","g":
            consoantss += 1
        default:
            otherss += 1
        }
    }
    return (vowelss, consoantss, otherss)
}

let total = count(string: "some arbitarary string!")
print(total)

//函数参数名称 上面例子，参数名string只能在函数体使用。这种类型的参数叫局部参数名。
//外部参数名 有时候，在调用函数时，给每个参数命名是非常有用的，如果你希望函数调用时提供参数名字，那就需要给每个参数除了局部参数名外再定义一个外部参数名。外部参数名写在局部参数名之前，用空格分隔。
func someFunction(_ localParam: Int) {
    
}

func join (os1 s1: String, os2 s2: String,  oj joiner: String) -> String {
    return s1 + joiner + s2
}

//join(s1: <#T##String#>, s2: <#T##String#>, joiner: <#T##String#>)
//join(os1: <#T##String#>, os2: <#T##String#>, oj: <#T##String#>)

//默认参数值 - 你可以在函数中给每个参数定义默认值。当默认值被定义后，调用函数可以忽略这个参数，注意：默认参数放在函数参数列表的最后。
func join1(string s1:String, toString s2:String, withJoiner joiner:String = " ") -> String {
    return s1 + joiner + s2
}

join1(string: "1", toString: "2")
join1(string: "1", toString: "2", withJoiner: "3")

func join2( s1:String,  s2:String,  joiner:String = " ") -> String {
    return s1 + joiner + s2
}

join2(s1: "1", s2: "2")
join2(s1: "1", s2: "2", joiner: " - ")

//可变参数 一个可变参数可接收一个或多个值。通过在变量类型后加...的方式来定义可变参数.传入可变参数的值在函数体内当成这个类型的一个数组，
func arithmeticMean(args: Double...) -> Double {
    var total = 0.0
    for arg in args {
        total += arg
    }
    return total / Double(args.count)
}

let res1 = arithmeticMean(args: 1,2,3,4,5)
print(res1)

let res2 = arithmeticMean(args: 3,8,19)
print(res2)

//注意，一个函数至多能有一个可变参数，而且他必须是参数表中的最后一个。这样做是为了函数调用不会出现歧义。如果函数有一个或多个带默认值的参数，而且还有一个可能参数，可变参数放在参数的最后。

//常量参数和变量参数 不支持了
//输入输出参数 如果想要一个函数可以修改参数的值，并且想要在这些修改在函数调用后结束仍然存在，那么应该把这个参数定义为输入输出参数,在参数定义前加inout.当传入的参数作为输入输出参数时，需要在参数前加&符号，表示这个值可以被函数修改
func swapTwoInt( num1: inout Int, num2: inout Int) {
    let tempA = num1
    num1 = num2
    num2 = tempA
}

var a = 1
var b = 2
swapTwoInt(num1: &a, num2: &b) //注意，你不能传入常量或字面量，因为常量、字面量不能被修改，而inout参数的目的是可以修改参数。
print("a = \(a), b = \(b)")

//函数类型，下面例子的函数类型是 (Int, Int) -> Int
func addTwoInts(a: Int, b:Int) -> Int {
    return a + b
}

func multiplyInts(a: Int, b: Int) -> Int {
    return a * b
}

//这个例子没有参数，没有返回值, 这个函数的类型是 () -> ()
func pirntHelloWord() {
    
}

//使用函数类型
var mathFunction:(Int, Int) -> Int = addTwoInts
//定义一个mathFunction的变量，类型是一个有两个Int型的参数并返回一个Int类型值的函数，并让这个新变量指向addTwoInts函数
print("result : \(mathFunction(2, 3))")
mathFunction = multiplyInts
print("result : \(mathFunction(2, 3))")
let antherFunction = addTwoInts
//antherFunction现在被推到出是(Int, Int) -> Int 类型

//函数类型作为参数类型
func printyMathResult(mathFunction:(Int, Int) -> Int ,a: Int, b:Int) {
    print("result is: \(mathFunction(a, b))")
}

printyMathResult(mathFunction: addTwoInts, a: 3, b: 5)

//函数类型作为返回类型 在 -> 后写一个完整的函数类型
func stepForward(input:Int) -> Int {
    return input + 1
}

func stepBackward(input: Int) -> Int {
    return input - 1
}

func chooseStepFunc(backward: Bool) -> (Int) -> Int {
    return backward ? stepBackward : stepForward
}

var curretnValue = 3
let moveNeareToZero = chooseStepFunc(backward: curretnValue > 0)

print("Counting to zero")
while curretnValue != 0 {
    print("\(curretnValue)")
    curretnValue = moveNeareToZero(curretnValue)
}

//嵌套函数  函数体里面定义函数

func chooseStepFunction2(backwards: Bool) -> (Int) -> Int {
    func stepForward(input:Int) -> Int {
        return input + 1
    }

    func stepBackward(input: Int) -> Int {
        return input - 1
    }
    
    return backwards ? stepBackward : stepForward
}

var c = -4
let move = chooseStepFunction2(backwards: c < 0)
while c != 0 {
    print("\(c)")
    c = move(c)
}
